Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[ios, macos] Add shapes annotations select/deselect delegates. #9755

Conversation

fabian-guerra
Copy link
Contributor

@fabian-guerra fabian-guerra commented Aug 11, 2017

Fixes #2082

The new delegates return a MGLShape annotation. The consideration is for polyllines you get the centroid (coordinate) for the whole line. The initial implementation I made returned a feature and the feature's centroid. I'm thinking add a delegate only for polylines. Thoughts?

@fabian-guerra fabian-guerra requested review from boundsj and 1ec5 August 11, 2017 12:59
@kkaefer kkaefer added the iOS Mapbox Maps SDK for iOS label Aug 16, 2017
@fabian-guerra fabian-guerra self-assigned this Aug 21, 2017
@fabian-guerra fabian-guerra added this to the ios-v3.6.3 milestone Aug 24, 2017
@param mapView The map view containing the annotation.
@param shapeAnnotation The shape annotation that was selected.
*/
- (void)mapView:(MGLMapView *)mapView didSelectShapeAnnotation:(MGLShape *)shapeAnnotation;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the existing methods are called -mapView:didSelectAnnotation: and -mapView:didDeselectAnnotation:, without referring to point annotations specifically, it’s interesting that this PR adds separate shape-specific methods.

On the one hand, I understand that suddenly passing shape annotations into a method that has long received only point annotations could be a surprise to some developers. On the other hand, we’ve never documented that prior limitation – it’s essentially a bug. There is precedent for opening up delegate methods to new data, namely when we started passing MGLUserLocation into -mapView:viewForAnnotation: in #5882.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #9755 (comment) I mention an alternative

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason behind naming this as didSelectShapeAnnotation is based on been clear to developers that this is not going to be called by selecting any *MGLPointAnnotation thus there is no callout view.

/// Mapping from a shape annotation object to shape layer id.
typedef std::map<id<MGLAnnotation>, std::string> MGLShapeAnnotationObjectLayerIDMap;

const NSString *MGLLayerIDShapeAnnotation = @"com.mapbox.annotations.shape.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @"" string literal NSString is already immutable. So this should be a constant pointer. As it stands, you can repoint MGLLayerIDShapeAnnotation and change its value. Also, this is only used in the implementation so it can be designated static. (i.e. static NSString *const MGLFoo = @"foo")

@@ -285,9 +290,11 @@ @implementation MGLMapView

MGLAnnotationTagContextMap _annotationContextsByAnnotationTag;
MGLAnnotationObjectTagMap _annotationTagsByAnnotation;

MGLShapeAnnotationObjectLayerIDMap _shapeAnnotationLayerIDs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd help readability later on to follow the convention (see _annotationTagsByAnnotation just above) and call this _shapeAnnotationLayerIDsByAnnotation

if(!annotation) {
return NO;
if(!annotation && !_selectedShapeAnnotation) {
MGLShape *shapeAnnotation = [self shapeAnnotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tapping a map with no annotations now crashes in -[MGLMapView shapeAnnotationForGestureRecognizer:] because the hitAnnotationTag is 0 (instead of MGLAnnotationTagNotFound) and that gets a nil result from [self annotationWithTag:hitAnnotationTag] which triggers an assert. I think the root cause is that -[MGLMapView shapeAnnotationTagsInRect:] has no _shapeAnnotationLayerIDs values to put in options so the _mbglMap->queryShapeAnnotations() actually returns some values instead of an empty list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thank you for catching this bug.

@param mapView The map view containing the annotation.
@param shapeAnnotation The shape annotation that was selected.
*/
- (void)mapView:(MGLMapView *)mapView didSelectShapeAnnotation:(MGLShape *)shapeAnnotation;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new APIs don't provide a shape "annotation". They do provide an MGLShape. Since they are intended for MGLPolyline(gon)(Feature) instances, should we remove the word "annotation" from the API and use a more specific type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MGLShape conforms to MGLAnnotation.

- (MGLShape*)shapeAnnotationForGestureRecognizer:(UITapGestureRecognizer*)singleTap
{
CGPoint tapPoint = [singleTap locationInView:self];
// CGRect queryRect = CGRectInset({ tapPoint, CGSizeZero },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These commented lines were pulled into methods. Don't forget to delete them here.

// Choose the first nearby annotation.
if (nearbyAnnotations.size())
{
hitAnnotationTag = nearbyAnnotations.front();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The older -[MGLMapView annotationTagAtPoint:persistingResults:] method has logic to cycle through points that re overlapping / nearby each other. Do we need to worry about that for shapes? What is the expected behavior if a user touches in an area where several polygons overlap or several polylines intersect? cc @1ec5

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s currently possible to disable touch interaction for a given point annotation using either MGLAnnotationImage.enabled. That’s a weird API, but do we need something like it for shape annotations?

@boundsj
Copy link
Contributor

boundsj commented Aug 30, 2017

@fabian-guerra wrt comments #9755 (comment) and #9755 (comment), it might be worth another look at the approach taken in #5502. That PR reuses the existing annotation delegate path (and -[MGLMapView annotationTagAtPoint:persistingResults:]) method.

cc @1ec5

[self deselectShapeAnnotation:_selectedShapeAnnotation];

_selectedShapeAnnotation = shapeAnnotation;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent of #2082 was that we’d display a callout over the selected shape. Per #2082 (comment), we’d need to constrain the coordinate to the current viewport before calculating the centroid. So coordinate wouldn’t give us quite the right behavior, but we could fashion a method that takes an MGLCoordinateBounds and clips the polygon to polyline to that bounds before passing the geometry off to Polylabel.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent of #2082 was that we’d display a callout over the selected shape.

Our Android SDK gained essentially the exact same functionality that is proposed here in #9443. I don't think there is any mechanism in that SDK to assist with callout placement, yet. I think it would be ok to use the same approach and keep #2082 open and handle callouts separately.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that would be an argument for adding a separate set of delegate methods, because it would be weird for -mapView:didSelectAnnotation: to work with -mapView:annotationCanShowCallout: in some cases but not others.

It doesn’t feel great to potentially have a point annotation and shape annotation both be selected at the same time. It’ll lock us into a design that’s reminiscent of MapKit but quite different, even after we implement the callout views.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My principal deterrent to avoid using [MGLMapView annotationTagAtPoint:persistingResults:] was the fact that *MGLShape "annotations" does not have a callout view. So selecting an annotation that handles a callout only in specific cases would be confusing for developers and interpreted as a bug.

But selecting a MGLShape annotation deselects MGLPointAnnotation and viceversa.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the broader question is whether shapes should have callouts. The original request in #2082 and much of the subsequent comments were about callouts, so I guess I’d consider that PR unfinished without them. If we land this feature without callouts and with an API that’s separate from normal annotation selection, does that make it more difficult for us to implement shape callouts later? Or are we counting on being able to clean up this API in v4.0.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the original request in #2082 was callouts. I should have referred to #3720 which is what the Android implementation #9443 fixes.

Maybe we could wait and refactor our Annotation API, then support callouts for shape annotations, this is how it looks right know so far.
annotation_api

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of a lift would it be to support callouts for shape annotations? At a glance, it looks like most of the pieces are already in place (especially polylabel).

@boundsj
Copy link
Contributor

boundsj commented Sep 18, 2017

Closing in favor of #9984

@boundsj boundsj closed this Sep 18, 2017
@lilykaiser lilykaiser removed this from the ios-v3.6.3 milestone Sep 29, 2017
@jfirebaugh jfirebaugh deleted the fabian-shape-polyline-v2-2082 branch October 24, 2017 20:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
iOS Mapbox Maps SDK for iOS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants